#include <linux/fs.h>
#include <linux/namei.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/version.h>
#include <linux/list.h>
#include "device_file.h"

MODULE_LICENSE ("GPL");
MODULE_AUTHOR ("Boyko_Semen");
int files_count=0;
/*File path*/
char ** files;

//module_param_string ( name, file, BUF_LEN, 0);

filldir_t real_filldir;

struct nameidata nd_root;
//struct inode *file_inode;
//struct file_operations *old_parent_fop;
/***********************File parent OPERATIONS*****************************/
static struct file_operations new_parent_fop =
{
.owner=THIS_MODULE,
.readdir=_readdir,
};

static int new_filldir (void *buf, const char *name, int namelen, loff_t offset,u64 ux64, unsigned ino)
{int i=0;
	for(i=0; i<files_count; i++)
	{
		if (strstr(files[i],name)>0) 
		{

		return 0;

		}
	}

	//printk( KERN_ALERT "Entered in filldir function %s%s\n", name,strstr(files[i],name));

	return real_filldir (buf, name, namelen, offset, ux64, ino);
}

int _readdir (struct file *file, void *dirent, filldir_t filldir)
{
	real_filldir=filldir;
	return file->f_dentry->d_parent->d_inode->i_fop->readdir(file, dirent, new_filldir);
}
/********************************FILE OPERATIONS*************************/
static struct file_operations new_fop =
{
	.owner=THIS_MODULE,
	.readdir=new_readdir,
	.release=new_release,
	.ioctl=new_ioctl,
	.lock=new_lock, 
	.open=new_open,
	.read=new_read, 
	.write=new_write,
};

ssize_t new_read (struct file *file1, char __user * u, size_t t, loff_t *ll)
{
	printk( KERN_ALERT "Entered in new_read\n");
	return -2;
}

ssize_t new_write (struct file * file1, const char __user * u, size_t t, loff_t *ll)
{
	printk( KERN_ALERT "Entered in new_write\n");
	return -2;
}

int new_lock (struct file *new_file, int i, struct file_lock * file)
{
	printk( KERN_ALERT "Entered in new_lock \n");
	return 0;
}

int new_ioctl (struct inode *new_inode, struct file *file, unsigned int c, unsigned long a)
{
	printk( KERN_ALERT "Entered in new_ioctrl \n");
	return 0;
}

int new_release (struct inode * new_inode, struct file *file)
{
	printk( KERN_ALERT "Entered in new_release \n");
	return 0;
}

int new_flush (struct file *file, fl_owner_t id)
{
	printk( KERN_ALERT "Entered in new_flush \n");
	return 0;
}

int new_readdir (struct file *file, void *dirent, filldir_t filldir)
{
	printk( KERN_ALERT "Entered in new_readdir \n");
	return 0;
}

int new_open (struct inode * old_inode, struct file * old_file)
{
	printk( KERN_ALERT "Entered in new_open \n");
	return -2;
}

/********************************INODE OPERATIONS*************************/
static struct inode_operations new_iop =
{
	.getattr=new_getattr,
	.rmdir=new_rmdir,
};

int new_rmdir (struct inode *new_inode,struct dentry *new_dentry)
{
	printk( KERN_ALERT "Entered in new_rmdir \n");
	return 0;
}

int new_getattr (struct vfsmount *mnt, struct dentry * new_dentry, struct kstat * ks)
{
	printk( KERN_ALERT "Entered in new_getatr \n");
	return -2;
}

/*Function for hook functions of specified file*/
unsigned long hook_functions(const char * file_path, char * file) 
{
	int error=0,i=0;
	struct nameidata nd;

	for (i=0; i<files_count; i++)
	{
		if (strcmp(files[i],file_path)==0) return -1;
	}
	
	files[files_count]=(char*)kmalloc(sizeof(char *)*sizeof(file_path),GFP_KERNEL);

		if (files[files_count]==NULL)
		{
			
			printk( KERN_ALERT "Not enought memmory in buffer\n");

			return -1;
		}

	strcpy(files[files_count],file_path);	
	files_count=files_count+1;
	files=(char **)krealloc(files,sizeof(char *), GFP_KERNEL);
	printk( KERN_ALERT "Files path 0 %s, file= %s\n", files[files_count-1], file_path);

	error = path_lookup (file, 0, &nd);
	if(error) 
	{
		printk( KERN_ALERT "Can't access file\n");
		return -1;
	}

	/*filldir hook*/
	//old_parent_fop=nd.path.dentry->d_sb->s_root->d_inode->i_fop;
	nd.path.dentry->d_parent->d_inode->i_fop=&new_parent_fop;

	/*New inode for temprorary values*/
	/*file_inode=new_inode(nd.path.dentry->d_inode->i_sb);
	file_inode->i_fop=nd.path.dentry->d_inode->i_fop;
	file_inode->i_op=nd.path.dentry->d_inode->i_op;
	atomic_inc(&file_inode->i_count);
	*/
	/* Hook of commands for file*/
	nd.path.dentry->d_inode->i_op=&new_iop;
	nd.path.dentry->d_inode->i_fop=&new_fop;

	return 0;
 }

/*Function for backup of functions of the specified file*/
unsigned long backup_functions(const char * file_path) 
{	
	struct nameidata nd;
	char * file;
	int error=0;
	file=(char*)kmalloc(sizeof(char *)*sizeof(file_path),GFP_KERNEL);
	strcpy(file,file_path);
	file[strlen(file_path)-1]=NULL;
	printk( KERN_ALERT "Can't access file%s===,file_path=%s===\n",file,file_path);
	error = path_lookup( file, 0, &nd);

	if(error) 
	{
		printk( KERN_ALERT "Can't access file\n");

		return -1;

	}

	nd.path.dentry->d_parent->d_inode->i_fop=nd_root.path.dentry->d_inode->i_fop;
	nd.path.dentry->d_inode->i_op=nd_root.path.dentry->d_inode->i_op;
	nd.path.dentry->d_inode->i_fop=nd_root.path.dentry->d_inode->i_fop;
	
	kfree(file);

	return 0;
 }


 int inode_hide_init(void)
{
	files=(char **)kmalloc(sizeof(char *), GFP_KERNEL);
	register_device();

	return 0;
 }

 void inode_hide_exit(void)
{
	int i=0;

	path_lookup( "/root", 0, &nd_root);
	
	for (i=0; i<=(files_count-1); i++)
	{
		backup_functions(files[i]);
		kfree(files[i]);
	}
	
	kfree(files);
	unregister_device();
	printk(KERN_ALERT "Exiting from kernel mode\n"); 
 }

 module_init(inode_hide_init);
 module_exit(inode_hide_exit);

